package com.yonyou.rabbitmq.config;

import com.yonyou.rabbitmq.common.MessageConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;

@Slf4j
@Configuration
public class QueueConfig {

    public DirectExchange defaultExchange;

    public DirectExchange dlxExchange;

    public Queue dlqQueue;

    public Binding bindingDlQueue;

    public CustomExchange delayedExchange;

    public Queue delayedQueue;

    public Binding bindingDelayedQueue;

    RabbitConfig rabbitConfig;

    public QueueConfig(RabbitConfig rabbitConfig) {
        this.rabbitConfig = rabbitConfig;
        this.defaultExchange = defaultExchange();
        this.dlxExchange = dlxExchange();
        this.dlqQueue = dlqQueue();
        this.bindingDlQueue = bindingDlQueue();
        this.delayedExchange = delayedExchange();
        this.delayedQueue = delayedQueue();
        this.bindingDelayedQueue = bindingDelayedQueue();
    }

    @Bean
    public Declarables declarables(){
        List<Declarable> list = new ArrayList<>();
        addDefault(list);
        Iterator<Map.Entry<String, RabbitConfig.QueueInfo>> iterator = rabbitConfig.getQueues().entrySet().iterator();
        while(iterator.hasNext()){
            RabbitConfig.QueueInfo queueInfo = iterator.next().getValue();
            if(StringUtils.isEmpty(queueInfo.getName())){
                continue;
            }
            String queueName = queueInfo.getName();
            Queue queue = QueueBuilder.durable(queueName).withArguments(bindingDlArgs(rabbitConfig.getTtl())).build();
            list.add(queue);
            Binding binding;
            if(queueInfo.isDelay()){
                binding = BindingBuilder.bind(queue).to(delayedExchange).with(queueName).noargs();
            }else{
                binding = BindingBuilder.bind(queue).to(defaultExchange).with(queueName);
            }
            list.add(binding);
        }
        // 有毒代码（勿动）
        list.add(this.delayedQueue);
        return new Declarables(list);
    }

    private void addDefault(List<Declarable> list) {
        list.add(this.defaultExchange);
        list.add(this.dlxExchange);
        list.add(this.dlqQueue);
        list.add(this.bindingDlQueue);
        list.add(this.delayedExchange);

        list.add(this.bindingDelayedQueue);
    }

    public DirectExchange defaultExchange() {
        return new DirectExchange(MessageConstants.DEFAULT_EXCHANGE,true,false);
    }

    public DirectExchange dlxExchange() {
        return new DirectExchange(MessageConstants.DLX_EXCHANGE,true,false);
    }

    public Queue dlqQueue() {
        return new Queue(MessageConstants.DLQ_QUEUE, true);//队列持久
    }

    public Binding bindingDlQueue() {
        return BindingBuilder.bind(this.dlqQueue).to(this.dlxExchange).with(MessageConstants.DLK_ROUTING_KEY);
    }

    public CustomExchange delayedExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put(MessageConstants.DELAYED_EXCHANGE_ARGS, MessageConstants.DIRECT);
        //参数二为类型：必须是x-delayed-message
        return new CustomExchange(MessageConstants.DELAYED_EXCHANGE_NAME, MessageConstants.DELAYED_EXCHANGE_TYPE, true, false, args);
    }

    public Queue delayedQueue(){
        return new Queue(MessageConstants.DELAYED_QUEUE_NAME, true, false, false, bindingDlArgs(rabbitConfig.getTtl()));
    }

    public Binding bindingDelayedQueue(){
        return BindingBuilder.bind(this.delayedQueue).to(this.delayedExchange).with(MessageConstants.DELAYED_QUEUE_NAME).noargs();
    }

    private Map<String, Object> bindingDlArgs(long ttl) {
        Map<String, Object> args = new HashMap<>(3);
        //声明死信交换器
        args.put(MessageConstants.DLX_EXCHANGE_ARGS, MessageConstants.DLX_EXCHANGE);
        //声明死信路由键
        args.put(MessageConstants.DLK_ROUTING_KEY_ARGS, MessageConstants.DLK_ROUTING_KEY);
        //声明队列消息过期时间 millSeconds
        args.put(MessageConstants.MESSAGE_TTL_ARGS, ttl);
        return args;
    }

}
